package Statistics;

import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;

import com.sleepycat.je.rep.monitor.NewMasterEvent;

import Config.GlobalLog;
import EurekaAnalyzer.IDInformation;
import EurekaAnalyzer.IDInformation.ScoredBoard;
import EurekaProcessor.Clusterer;
import Store.StoreManager;

public class IDGraphGlobalProperty implements Serializable{
	private static int board_property_count = 5;
	private int     nodes;
	private int     edges;
	public class DegreeRecord implements Serializable{
		public int degree;
		public int count;
		public double percent;
	}
	public class ScoredBoard implements Serializable{
		public String board;
		public int   appearanceCnt = 0;
	}
	private Set<Set<String>> clusters;
	
	private int scc_count;
	private int cluster_count;
	private ScoredBoard[][] sccInfo;     //scc_count * board_property_count
	private ScoredBoard[][] clusterInfo; //cluster_count * board_property_count;
	private CompositeGraph  sccView;
	private CompositeGraph  clusterView; 
	
	public class SortByInDegree implements Comparator<IDGraphNode>{
		@Override
		public int compare(IDGraphNode left, IDGraphNode right) {
			return left.GetInDegree()-right.GetInDegree();
		}
	}
	public class SortByOutDegree implements Comparator<IDGraphNode>{
		@Override
		public int compare(IDGraphNode left, IDGraphNode right) {
			return left.GetOutDegree()-right.GetOutDegree();
		}
	}
	private Vector<DegreeRecord> inDegreeDistribution = new Vector<DegreeRecord>();
	private Vector<DegreeRecord> outDegreeDistribution = new Vector<DegreeRecord>();
	
	public Vector<DegreeRecord> GetInDegreeDistribution(){return inDegreeDistribution;}
	public Vector<DegreeRecord> GetOutDegreeDistribution(){return outDegreeDistribution;}
	
	public IDGraphGlobalProperty(IDGraph graph,StoreManager mgr)
	{
		CalculateDegreeDistribution(graph);
	}
	
	public void CalculateDegreeDistribution(IDGraph graph)
	{
		List<IDGraphNode> nodes = new ArrayList<IDGraphNode>(graph.GetNodeMap().values());
		SortByInDegree sortByInDegree = new SortByInDegree();
		Collections.sort(nodes, sortByInDegree);
        GlobalLog.AddStat("The IN DEGREE Distribution:");
        int degree = nodes.get(0).GetInDegree();
        int count  = 1;
        for(int i=1;i<nodes.size();i++)
		{
			if(degree == nodes.get(i).GetInDegree())
			{
				count++;
			}else {
				DegreeRecord dr = new DegreeRecord();
				dr.degree = degree;
				dr.count  = count;
				dr.percent = (double)count/(double)nodes.size();
				inDegreeDistribution.add(dr);
				GlobalLog.AddStat(degree+" : "+count+" : "+(double)count/(double)nodes.size());
				degree = nodes.get(i).GetInDegree();
				count  = 1;
			}
		}
		DegreeRecord dr = new DegreeRecord();
		dr.degree = degree;
		dr.count  = count;
		dr.percent = (double)count/(double)nodes.size();
		inDegreeDistribution.add(dr);
        GlobalLog.AddStat(degree+" : "+count+" : "+(double)count/(double)nodes.size());
        
        
        
		SortByOutDegree sortByOutDegree = new SortByOutDegree();
		Collections.sort(nodes, sortByOutDegree);
        GlobalLog.AddStat("The OUT DEGREE Distribution:");
        degree = nodes.get(0).GetOutDegree();
        count  = 1;
        for(int i=1;i<nodes.size();i++)
		{
			if(degree == nodes.get(i).GetOutDegree())
			{
				count++;
			}else {
				DegreeRecord outdr = new DegreeRecord();
				outdr.degree = degree;
				outdr.count  = count;
				outdr.percent = (double)count/(double)nodes.size();
				outDegreeDistribution.add(outdr);
				GlobalLog.AddStat(degree+" : "+count+" : "+(double)count/(double)nodes.size());
				degree = nodes.get(i).GetOutDegree();
				count  = 1;
			}
		}
        DegreeRecord outdrr = new DegreeRecord();
		outdrr.degree = degree;
		outdrr.count  = count;
		outdrr.percent = (double)count/(double)nodes.size();
		outDegreeDistribution.add(outdrr);
        GlobalLog.AddStat(degree+" : "+count+" : "+(double)count/(double)nodes.size());
	}
	public void CalculateClusters(IDGraph graph, int stop)
	{
		clusters = Clusterer.HierarchicalClustering(graph, stop);
	}
	
	public void CalculateSCCInfo(Set<Set<String>> sets,StoreManager mgr)
	{
		scc_count = sets.size();
		sccInfo = new ScoredBoard[scc_count][board_property_count];
		int index = 0;
		HashMap<String, Integer> hashmap = new HashMap<String, Integer>();
		for(Set<String> scc:sets)
		{
			int total = 0;
			for (String id:scc) {
				IDInformation idi = mgr.GetIDInfoByID(id);
				Vector<IDInformation.ScoredBoard> boards = (Vector<IDInformation.ScoredBoard>) idi.GetBoards();
				for(IDInformation.ScoredBoard sb:boards)
				{
					int tmp = 0;
					if(hashmap.containsKey(sb.board))
						tmp = hashmap.remove(sb.board);
					tmp += sb.appearanceCnt;
					hashmap.put(sb.board, tmp);
					total += sb.appearanceCnt;
				}
			}
			
			int all = 0;
		    int lastindex = 0;
			for(int j = 0;j<board_property_count-1;j++)
			{
				Set<Entry<String, Integer>> set = hashmap.entrySet();
				int max = -1;
				String tmpString = null;
				for(Entry<String, Integer> entry:set)
				{
					if(entry.getValue()>max)
						tmpString = entry.getKey();
				}
				if(max == -1)
					break;
				all += max;
				hashmap.remove(tmpString);
				ScoredBoard sBoard = new ScoredBoard();
				sBoard.board = tmpString;
				sBoard.appearanceCnt = max;
				sccInfo[index][j] = sBoard;
				lastindex = j;
			}
			ScoredBoard sBoard = new ScoredBoard();
			sBoard.board = "Other";
			sBoard.appearanceCnt = total-all;
			sccInfo[index][lastindex+1] = sBoard;
			hashmap.clear();
			index++;
		}
	}
	
	/**
	 * This function is just for test...
	 */
	void ShowSCCInfo()
	{
		
	}
}
